// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Threading.Tasks;
using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;

namespace SnapObjects.Data.Oracle
{
    public class OracleAdoDbDataVisitor : AdoDbDataVisitor
    {
        private bool _isDisposed = false;

        public OracleAdoDbDataVisitor(
            OracleCommand oracleCommand,
            OracleDataReader oracleDataReader,
            IEnumerable<IAdoDbParameter> parameters,
            IDataContextOptions contextOptions)
            : base(oracleCommand, oracleDataReader, parameters, contextOptions)
        {
        }

        public override void Dispose()
        {
            if (_isDisposed)
            {
                return;
            }

            _isDisposed = true;

            if (_reader != null)
            {
                if (!_reader.IsClosed)
                {
                    _reader.Close();

                    this.SetValueForOutputParameters();
                }

                _reader.Dispose();
            }

            if (_command != null)
            {
                if (_command.Connection.State != ConnectionState.Closed)
                {
                    _command.Cancel();
                    _command.Dispose();
                }
            }
        }

        public override async Task DisposeAsync()
        {
            if (_isDisposed)
            {
                return;
            }

            _isDisposed = true;

            if (_reader != null)
            {
                if (!_reader.IsClosed)
                {
                    await _reader.CloseAsync();

                    this.SetValueForOutputParameters();
                }

                await _reader.DisposeAsync();
            }

            if (_command != null)
            {
                if (_command.Connection.State != ConnectionState.Closed)
                {
                    _command.Cancel();

                    await _command.DisposeAsync();
                }
            }
        }

        protected override void SetValueForOutputParameters()
        {
            if (_outputParameters.Count < 1 ||
                _command == null ||
                _command.Parameters.Count < 1)
            {
                return;
            }

            foreach (var parameter in _outputParameters)
            {
                if (parameter.ClrType != null && parameter.ClrType == typeof(ICursor))
                {
                    continue;
                }

                var dbParameter = this.FindDbParamter(parameter.ParameterName);

                if (dbParameter != null)
                {
                    try
                    {
                        if (dbParameter.Value is OracleString str)
                        {
                            ((AdoDbParameter)parameter).Value = str.Value;
                        }
                        else if (dbParameter.Value is OracleDate date)
                        {
                            ((AdoDbParameter)parameter).Value = date.Value;
                        }
                        else if (dbParameter.Value is OracleDecimal dec)
                        {
                            ((AdoDbParameter)parameter).Value = dec.Value;
                        }
                        else if (dbParameter.Value is OracleTimeStamp ts)
                        {
                            ((AdoDbParameter)parameter).Value = ts.Value;
                        }
                        else if (dbParameter.Value is OracleTimeStampLTZ ltz)
                        {
                            ((AdoDbParameter)parameter).Value = ltz.Value;
                        }
                        else if (dbParameter.Value is OracleTimeStampTZ tz)
                        {
                            ((AdoDbParameter)parameter).Value = tz.Value;
                        }
                        else if (dbParameter.Value is OracleBoolean boolean)
                        {
                            ((AdoDbParameter)parameter).Value = boolean.Value;
                        }
                        else if (dbParameter.Value is OracleBinary binary)
                        {
                            ((AdoDbParameter)parameter).Value = binary.Value;
                        }
                        else if (dbParameter.Value is OracleIntervalDS ids)
                        {
                            ((AdoDbParameter)parameter).Value = ids.Value;
                        }
                        else if (dbParameter.Value is OracleIntervalYM iym)
                        {
                            ((AdoDbParameter)parameter).Value = iym.Value;
                        }
                        else if (dbParameter.Value is OracleBlob blob)
                        {
                            ((AdoDbParameter)parameter).Value = blob.Value;
                        }
                        else if (dbParameter.Value is OracleClob clob)
                        {
                            ((AdoDbParameter)parameter).Value = clob.Value;
                        }
                        else if (dbParameter.Value is OracleBFile bFile)
                        {
                            ((AdoDbParameter)parameter).Value = bFile.Value;
                        }
                        else
                        {
                            ((AdoDbParameter)parameter).Value = dbParameter.Value;
                        }
                    }
                    catch
                    {
                        //TODO...
                    }
                }
            }
        }

        private DbParameter FindDbParamter(string parameterName)
        {
            if (_command.Parameters.Contains(parameterName))
            {
                return _command.Parameters[parameterName];
            }

            if (!String.IsNullOrWhiteSpace(parameterName) && parameterName.StartsWith(OracleSqlGenerationHelper.PARAMETERIDENTIFIER + ""))
            {
                var tmpName = parameterName.TrimStart(OracleSqlGenerationHelper.PARAMETERIDENTIFIER);

                if (_command.Parameters.Contains(tmpName))
                {
                    return _command.Parameters[tmpName];
                }
            }

            return null;
        }
    }
}
